package com.tlgen.rpc.utils;

import com.tlgen.rpc.config.OkHttpProperties;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import static com.tlgen.rpc.utils.OkHttpUtils.*;

public class MappingUtils {

    public static String toMap(Method method, Object[] args) {
        Map<Class<? extends Annotation>, Function<Object[], String>> map = new HashMap<>();
        map.put(GetMapping.class, (x) -> handleGet(method, args));
        map.put(PostMapping.class, (x) -> handlePostPutDelete(method, args));
        map.put(PutMapping.class, (x) -> handlePostPutDelete(method, args));
        map.put(DeleteMapping.class, (x) -> handlePostPutDelete(method, args));
        String apply = null;
        for (Map.Entry<Class<? extends Annotation>, Function<Object[], String>> entry : map.entrySet()) {
            if (method.isAnnotationPresent(entry.getKey())) {
                apply = entry.getValue().apply(args);
            }
        }
        return apply;
    }

    private static String handleGet(Method method, Object[] args) {
        Parameter[] parameters = method.getParameters();
        StringBuilder builder = new StringBuilder();
        Map<String, String> map = new HashMap<>();
        // 获取 path
        String path = getRequestPath(method);
        // 判断 path 是否存在 {}, 如果存在则代表该方法是由 @PathVariable 注解修饰了参数
        if (path.contains("{")) {
            path = replacePathVariable(args, path, map);
        } else {
            for (int i = 0; i < parameters.length; i++) {
                builder.append(parameters[i].getName())
                        .append("=")
                        .append(args[i])
                        .append("&");
            }
        }
        return doGet(path, method, builder.toString());
    }

    @NotNull
    private static String replacePathVariable(Object[] args, String path, Map<String, String> map) {
        String[] split = path.split("/\\{");
        // 从 1 开始是把 path 去掉
        for (int i = 1; i < split.length; i++) {
            String name = split[i].substring(0, split[i].length() - 1);
            map.put(name, String.valueOf(args[i - 1])); // i - 1 代表参数还是正常从下标 0 开始取
        }
        // 将 path 的动态占位参数替换, 如 /selectByMobile/{mobile}/{flag}
        for (Map.Entry<String, String> entry : map.entrySet()) {
            path = path.replace("{" + entry.getKey() + "}", entry.getValue());
        }
        return path;
    }

    private static String handlePostPutDelete(Method method, Object[] args) {
        // 获取 path
        String path = getRequestPath(method);
        Map<String, String> map = new HashMap<>();
        Parameter[] parameters = method.getParameters();
        // 先判断是否是 @RequestBody 修饰的参数
        if (parameters[0].isAnnotationPresent(
                org.springframework.web.bind.annotation.RequestBody.class
        )) {
            return doByJson(path, method, args[0]);
        } else {
            // 判断 path 是否存在 {}, 如果存在则代表该方法是由 @PathVariable 注解修饰了参数
            if (path.contains("{")) {
                path = replacePathVariable(args, path, map);
            } else {
                // 是 @RequestParam 修饰的参数
                for (int i = 0; i < parameters.length; i++) {
                    if (parameters[i].isAnnotationPresent(RequestParam.class)) {
                        map.put(parameters[i].getName(), String.valueOf(args[i]));
                    }
                }
            }
        }
        return doByForm(path, method, map);
    }

    @NotNull
    private static String getRequestPath(Method method) {
        GetMapping get = method.getAnnotation(GetMapping.class);
        PostMapping post = method.getAnnotation(PostMapping.class);
        PutMapping put = method.getAnnotation(PutMapping.class);
        DeleteMapping delete = method.getAnnotation(DeleteMapping.class);
        return Optional.ofNullable(get).map(GetMapping::value).map(v -> v[0]).orElse(
                Optional.ofNullable(post).map(PostMapping::value).map(x -> x[0]).orElse(
                        Optional.ofNullable(put).map(PutMapping::value).map(x -> x[0]).orElse(
                                Optional.ofNullable(delete).map(DeleteMapping::value).map(x -> x[0]).orElse("")
                        )
                )
        );
    }

    @Nullable
    public static Request createRequest(Method method, String url, okhttp3.RequestBody body) {
        boolean isPost = method.isAnnotationPresent(PostMapping.class);
        boolean isPut = method.isAnnotationPresent(PutMapping.class);
        boolean isDelete = method.isAnnotationPresent(DeleteMapping.class);
        Request request = null;
        if (isPost)
            request = new Request.Builder()
                    .url(url)
                    .post(body)
                    .build();
        if (isPut)
            request = new Request.Builder()
                    .url(url)
                    .put(body)
                    .build();
        if (isDelete)
            request = new Request.Builder()
                    .url(url)
                    .delete(body)
                    .build();
        return request;
    }


    @NotNull
    public static OkHttpClient createOkHttpClient() {
        OkHttpProperties properties = SpringUtils.getBean(OkHttpProperties.class);
        return new OkHttpClient().newBuilder()
                .connectTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS)
                .readTimeout(properties.getReadTimeout(), TimeUnit.SECONDS)
                .writeTimeout(properties.getWriteTimeout(), TimeUnit.SECONDS)
                .build();
    }

}
